﻿using System.Drawing;
using System;
using System.IO;

namespace GeoSketch2.Core
{
    /// <summary>
    /// A geotagged image, contains its transformation beetween image coordinates and object coodinates
    /// </summary>
    public class GeoTaggedImage : BaseDrawableObject
    {
        #region XMLElementsName
#pragma warning disable 1591
        public const string XML_ImageExtents = "ImageExtents";
#pragma warning restore 1591
        #endregion

        #region Fields
        /// <summary>
        /// The image
        /// </summary>
        public Bitmap Image;

        /// <summary>
        /// The transformation beetween object coordinates and image coodinates
        /// </summary>
        public Transformations.AffineTransformation2D Object2ImageTransformation;

        private RectangleD _ImageExtents;

        /// <summary>
        /// The full filename of the picture
        /// </summary>
        public string FullFileName;
        #endregion

        #region Constructor
        /// <summary>
        /// A geotagged image, contains its transformation beetween image coordinates and object coodinates
        /// </summary>    
        public GeoTaggedImage(Bitmap Image, Layer Layer)
        :base(Layer)
        {
            Object2ImageTransformation = new Transformations.AffineTransformation2D();
            if (Image != null)
            {
                this.Image = Image;
                ImageExtents = new RectangleD(0, 0, Image.Width, Image.Height, YAxisDirection.DownSide);
                ImageExtents.YaxisDirection = YAxisDirection.UpSide;
                ComputeTransformation();
            }
        }

        /// <summary>
        /// A geotagged image, contains its transformation beetween image coordinates and object coodinates
        /// </summary>   
        public GeoTaggedImage(string FullFileName,Layer Layer)
            :this(new Bitmap(FullFileName),Layer)
        {
            this.FullFileName = FullFileName;
        }
        #endregion

        #region Methods
        /// <summary>
        /// The draw method
        /// </summary>
        /// <param name="Viewport">The viewport to draw to</param>
        protected override void DrawUnselected(BaseViewport Viewport)
        {
            DrawCustomPen(Viewport, new Pen(Color.Transparent, 0f));
        }

        /// <summary>
        /// The draw method
        /// </summary>
        /// <param name="Viewport">The viewport to draw to</param>
        protected override void DrawSelected(BaseViewport Viewport)
        {
            DrawCustomPen(Viewport, Vars.Setup.Tools.TSelection.SelectionPen);
        }

        /// <summary>
        /// The draw method for each object with a custom pen
        /// </summary>
        /// <param name="Viewport">The viewport to draw to</param>
        /// <param name="CustomPen">The custom pen object to draw the object with</param>
        public override void DrawCustomPen(BaseViewport Viewport, Pen CustomPen)
        {
            RectangleD ClientRectangle = Viewport.DisplayControl.ClientRectangle;
            RectangleD ClientRectangleOnImage = Viewport.Object2ImageTransformation.InverseTransform(ClientRectangle, YAxisDirection.UpSide);
            ClientRectangleOnImage = Object2ImageTransformation.Transform(ClientRectangleOnImage, YAxisDirection.DownSide);
            Viewport.Graphic.DrawImage(Image, (RectangleF)ClientRectangle, (RectangleF)ClientRectangleOnImage, GraphicsUnit.Pixel);
            Viewport.Graphic.DrawRectangle(CustomPen, (Rectangle)Viewport.Object2ImageTransformation.Transform(_ImageExtents, YAxisDirection.DownSide));
        }

        /// <summary>
        /// The method to test if an object is near to the point clicked, for an object selection purpose
        /// </summary>
        /// <param name="Clic">The point clicked in object coordinates</param>
        /// <param name="IsNearDistance">The distance under which the clic is near to the object</param>
        public override bool IsNear(Point2D64D Clic, double IsNearDistance)
        {
            Point2D64D center;

            if (Clic.DistancePointLine(_ImageExtents.UpperLeftPoint, _ImageExtents.UpperRightPoint) < IsNearDistance)
            {
                center = (_ImageExtents.UpperLeftPoint + _ImageExtents.UpperRightPoint) * 0.5;

                if (2 * center.Distance2DSqr(Clic) < _ImageExtents.UpperLeftPoint.Distance2DSqr(_ImageExtents.UpperRightPoint) + Core.Vars.Setup.Tools.TSelection.IsNearDistance * Core.Vars.Setup.Tools.TSelection.IsNearDistance)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else if (Clic.DistancePointLine(_ImageExtents.UpperRightPoint, _ImageExtents.LowerRightPoint) < IsNearDistance)
            {
                center = (_ImageExtents.UpperRightPoint + _ImageExtents.LowerRightPoint) * 0.5;

                if (2 * center.Distance2DSqr(Clic) < _ImageExtents.UpperRightPoint.Distance2DSqr(_ImageExtents.LowerRightPoint) + Core.Vars.Setup.Tools.TSelection.IsNearDistance * Core.Vars.Setup.Tools.TSelection.IsNearDistance)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else if (Clic.DistancePointLine(_ImageExtents.LowerRightPoint, _ImageExtents.LowerLeftPoint) < IsNearDistance)
            {
                center = (_ImageExtents.LowerRightPoint + _ImageExtents.LowerLeftPoint) * 0.5;

                if (2 * center.Distance2DSqr(Clic) < _ImageExtents.LowerRightPoint.Distance2DSqr(_ImageExtents.LowerLeftPoint) + Core.Vars.Setup.Tools.TSelection.IsNearDistance * Core.Vars.Setup.Tools.TSelection.IsNearDistance)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else if (Clic.DistancePointLine(_ImageExtents.LowerLeftPoint, _ImageExtents.UpperLeftPoint) < IsNearDistance)
            {
                center = (_ImageExtents.LowerLeftPoint + _ImageExtents.UpperLeftPoint) * 0.5;

                if (2 * center.Distance2DSqr(Clic) < _ImageExtents.LowerLeftPoint.Distance2DSqr(_ImageExtents.UpperLeftPoint) + Core.Vars.Setup.Tools.TSelection.IsNearDistance * Core.Vars.Setup.Tools.TSelection.IsNearDistance)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Returns the nearest line to the click, null if none
        /// </summary>
        /// <param name="Clic">The point clicked in object coordinates</param>
        /// <param name="IsNearDistance">The distance under which the clic is near to the object</param>
        public override Line2D64D NearestLine(Point2D64D Clic, double IsNearDistance)
        {
            Point2D64D center;

            if (Clic.DistancePointLine(_ImageExtents.UpperLeftPoint, _ImageExtents.UpperRightPoint) < IsNearDistance)
            {
                center = (_ImageExtents.UpperLeftPoint + _ImageExtents.UpperRightPoint) * 0.5;

                if (2 * center.Distance2DSqr(Clic) < _ImageExtents.UpperLeftPoint.Distance2DSqr(_ImageExtents.UpperRightPoint) + Core.Vars.Setup.Tools.TSelection.IsNearDistance * Core.Vars.Setup.Tools.TSelection.IsNearDistance)
                {
                    return new Line2D64D(_ImageExtents.UpperLeftPoint, _ImageExtents.UpperRightPoint);
                }
                else
                {
                    return null;
                }
            }
            else if (Clic.DistancePointLine(_ImageExtents.UpperRightPoint, _ImageExtents.LowerRightPoint) < IsNearDistance)
            {
                center = (_ImageExtents.UpperRightPoint + _ImageExtents.LowerRightPoint) * 0.5;

                if (2 * center.Distance2DSqr(Clic) < _ImageExtents.UpperRightPoint.Distance2DSqr(_ImageExtents.LowerRightPoint) + Core.Vars.Setup.Tools.TSelection.IsNearDistance * Core.Vars.Setup.Tools.TSelection.IsNearDistance)
                {
                    return new Line2D64D(_ImageExtents.UpperRightPoint, _ImageExtents.LowerRightPoint);
                }
                else
                {
                    return null;
                }
            }
            else if (Clic.DistancePointLine(_ImageExtents.LowerRightPoint, _ImageExtents.LowerLeftPoint) < IsNearDistance)
            {
                center = (_ImageExtents.LowerRightPoint + _ImageExtents.LowerLeftPoint) * 0.5;

                if (2 * center.Distance2DSqr(Clic) < _ImageExtents.LowerRightPoint.Distance2DSqr(_ImageExtents.LowerLeftPoint) + Core.Vars.Setup.Tools.TSelection.IsNearDistance * Core.Vars.Setup.Tools.TSelection.IsNearDistance)
                {
                    return new Line2D64D(_ImageExtents.LowerRightPoint, _ImageExtents.LowerLeftPoint);
                }
                else
                {
                    return null;
                }
            }
            else if (Clic.DistancePointLine(_ImageExtents.LowerLeftPoint, _ImageExtents.UpperLeftPoint) < IsNearDistance)
            {
                center = (_ImageExtents.LowerLeftPoint + _ImageExtents.UpperLeftPoint) * 0.5;

                if (2 * center.Distance2DSqr(Clic) < _ImageExtents.LowerLeftPoint.Distance2DSqr(_ImageExtents.UpperLeftPoint) + Core.Vars.Setup.Tools.TSelection.IsNearDistance * Core.Vars.Setup.Tools.TSelection.IsNearDistance)
                {
                    return new Line2D64D(_ImageExtents.LowerLeftPoint, _ImageExtents.UpperLeftPoint);
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Returns the nearest point to the click, null if none
        /// </summary>
        /// <param name="Clic">The point clicked in object coordinates</param>
        /// <param name="IsNearDistance">The distance under which the clic is near to the object</param>
        public override Point2D64D NearestPoint(Point2D64D Clic, double IsNearDistance)
        {
            if (Clic.Distance2D(_ImageExtents.LowerLeftPoint) < IsNearDistance)
            {
                return _ImageExtents.LowerLeftPoint;
            }
            else if (Clic.Distance2D(_ImageExtents.LowerRightPoint) < IsNearDistance)
            {
                return _ImageExtents.LowerRightPoint;
            }
            else if (Clic.Distance2D(_ImageExtents.UpperLeftPoint) < IsNearDistance)
            {
                return _ImageExtents.UpperLeftPoint;
            }
            else if (Clic.Distance2D(_ImageExtents.UpperRightPoint) < IsNearDistance)
            {
                return _ImageExtents.UpperRightPoint;
            }
            else
            {
                return null;
            }
        }

        //Computes the transformation between the object coordinates of the image and the images coordinates
        private void ComputeTransformation()
        {
            Object2ImageTransformation.ComputeTranformationFromTargetArea(_ImageExtents, new Rectangle(new Point(), Image.Size));
        }

        /// <summary>
        /// Translates the object
        /// </summary>
        /// <param name="Vector">The translation vector</param>
        public override void Translate(Point2D64D Vector)
        {
            ImageExtents.X += Vector.X;
            ImageExtents.Y += Vector.Y;
        }

        /// <summary>
        /// Clones the object
        /// </summary>
        public override T Clone<T>()
        {
            GeoTaggedImage img = new GeoTaggedImage(Image,Layer);
            img.ImageExtents=ImageExtents.DeepClone();
            return (T)(object)img;
        }
        #endregion

        #region Properties
        /// <summary>
        /// The extents of the image in object coordinates, the rectangle must be upside oriented
        /// </summary>
        public RectangleD ImageExtents
        {
            get
            {
                return _ImageExtents;
            }
            set
            {
                _ImageExtents = value;
                ComputeTransformation();
            }
        }

        /// <summary>
        /// Gets the bounding box of the object, the Y-axis direction of the REctangleD must be upside
        /// </summary>
        public override RectangleD Bounds
        {
            get
            {
                return ImageExtents;
            }
        }

        /// <summary>
        /// Gets the filename of the picture, without the path
        /// </summary>
        public string Filename
        {
            get
            {
                return Path.GetFileName(this.FullFileName);
            }
        }
        #endregion
    }
}
